package com.zlp.aop;

import com.zlp.annotaion.SysWebControllerExceptionLog;
import com.zlp.annotaion.SysWebControllerLog;
import com.zlp.dao.SysRequestExceptionLogMapper;
import com.zlp.dao.SysWebRequestLogMapper;
import com.zlp.entity.SysRequestExceptionLogWithBLOBs;
import com.zlp.entity.SysWebRequestLogWithBLOBs;
import com.zlp.utils.IPUtil;
import com.zlp.utils.ParamData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 
 * @author:ZouLiPing
 * @Description:操作日志记录
 * @date :2019年8月14日14:57:27
 */
@Aspect
@Component
@Slf4j
public class SystemLogAspect {
	

	@Autowired
	private SysWebRequestLogMapper requestLogMapper;
	
	@Autowired
	private SysRequestExceptionLogMapper requestExceptionLogMapper;

	@Resource
	private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * Controller层切点 使用到了定义的 SysWebControllerLog 作为切点表达式。
     */	
    @Pointcut("@annotation(com.zlp.annotaion.SysWebControllerLog)")
    public void controllerLogAspect() {
    }

    /**
     * Controller层切点 使用到了定义的 SysWebControllerExceptionLog 作为切点表达式。
     */
    @Pointcut("@annotation(com.zlp.annotaion.SysWebControllerExceptionLog)")
    public void controllerExceptionLogAspect() {
    }



	/**
	 * 前置通知 用于拦截Controller层记录用户的操作
	 * @param joinPoint 切点
	 */
	@Before("controllerLogAspect()")
	public void doBefore(JoinPoint joinPoint) {
		getLog(joinPoint, null);
	}

	/**
	 * 异常通知 用于拦截执行方法层记录异常日志
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "controllerExceptionLogAspect()", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
		getLog(joinPoint, e);
	}

	
	private void getLog(JoinPoint joinPoint, Throwable e){
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		ParamData params = new ParamData();
		// 读取session中的用户
		String url = request.getRequestURL().toString();
		String userName = null;
		String account = null;
		Integer userId = null;
		// 获得目标方法的参数
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0 ) {
        	Object object = args[0];
        	if(object != null) {
        		if (args[0].getClass() == DefaultMultipartHttpServletRequest.class) {
	        		DefaultMultipartHttpServletRequest request5 = (DefaultMultipartHttpServletRequest) joinPoint.getArgs()[0];
	        		Map<String, String> resultInfo = getResultInfo(request5);
	        		params.putAll(resultInfo);
        		}
        	}
        }
		SysWebRequestLogWithBLOBs requestLog = new SysWebRequestLogWithBLOBs();
		requestLog.setUserId(userId);
		requestLog.setUserName(userName);
		requestLog.setUserToken(account);
		requestLog.setType(0);
		requestLog.setUrl(url);
		requestLog.setMethod((joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));
		requestLog.setParams(params.toString());
		requestLog.setSysRequestIp(request.getRemoteAddr());
		requestLog.setRequestIp(IPUtil.getIpAdd(request));
		requestLog.setOperDate(new Date());
		try {
			if(null == e){
				SysWebControllerLog logAnnotation = getControllerAnnotation(joinPoint);
				requestLog.setSysType(logAnnotation.sysKey());
				requestLog.setSysTypeName(logAnnotation.SysTypeName());
				requestLog.setDescription(logAnnotation.description());
			}else{
				requestLog.setDetail(e.toString());
				SysWebControllerExceptionLog logAnnotation = getSysWebControllerExceptionLog(joinPoint);
				requestLog.setSysType(logAnnotation.sysKey());
				requestLog.setSysTypeName(logAnnotation.SysTypeName());
				requestLog.setDescription(logAnnotation.description());
			}
			// 系统模块
			if(requestLog.getSysType() >= 1){
				if(null == e) {
					// 异步形式写入日志到数据库中
					threadPoolTaskExecutor.execute(new SysLogThread(requestLog));
				}else {
					requestLog.setType(2);
					// 异步形式写入日志到数据库中
//					threadPoolTaskExecutor.execute(new SysLogThread(requestLog));
					threadPoolTaskExecutor.execute(new Runnable() {
						@Override
						public void run() {
							insertSysLog(requestLog);
						}
					});
				}
			}
		} catch (Exception ex) {
			// 记录本地异常日志
			log.error("error",ex);
			log.error("异常信息:{}", ex.getMessage());
		}
	}

	/**
	 *
	 * 获取注解中对方法的描述信息 用于SysWebControllerExceptionLog层注解
	 * @param joinPoint 切点
	 * @throws Exception
	 *
	 */
	private static SysWebControllerExceptionLog getSysWebControllerExceptionLog(JoinPoint joinPoint) throws Exception {

		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		SysWebControllerExceptionLog logAnnotation = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					logAnnotation = method.getAnnotation(SysWebControllerExceptionLog.class);
					break;
				}
			}
		}
		return logAnnotation;

	}

	/**
	 * 使用多线程写入日志目的：加快响应 提高程序效率 使用线程池维护线程
	 */
	class SysLogThread implements Runnable {
		private SysWebRequestLogWithBLOBs requestLog;

		public SysLogThread(SysWebRequestLogWithBLOBs requestLog) {
			this.requestLog = requestLog;
		}
		@Override
		public void run() {
			insertSysLog(requestLog);
		}
	}
	
	private void insertSysLog(SysWebRequestLogWithBLOBs requestLog) {
		if(StringUtils.isEmpty(requestLog.getDetail())) {
			// 保存数据库 操作日志
			requestLogMapper.insertSelective(requestLog);
		}else {
			SysRequestExceptionLogWithBLOBs exceptionLog = new SysRequestExceptionLogWithBLOBs();
			try {
				BeanUtils.copyProperties(exceptionLog, requestLog);
				requestExceptionLogMapper.insertSelective(exceptionLog);
			} catch (Exception e) {
				e.printStackTrace();
				log.error("error{}",e);
			}
		}
	}
	
	  /**
	   * 获取DefaultMultipartHttpServletRequest信息
	   * @param request
	   * @return
	   */
	  public static Map<String, String> getResultInfo(DefaultMultipartHttpServletRequest request) {
	    Map requestParams = request.getParameterMap();
	    Map<String,String> params = new HashMap<String,String>();
	    for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
	      String name = (String) iter.next();
	      String[] values = (String[]) requestParams.get(name);
	      String valueStr = "";
	      for (int i = 0; i < values.length; i++) {
	        valueStr = (i == values.length - 1) ? valueStr + values[i]
	            : valueStr + values[i] + ",";
	      }
	      params.put(name, valueStr);
	    }
	    return params;
	  }

	/**
	 *
	 * 获取注解中对方法的描述信息 用于Controller层注解
	 * @param joinPoint 切点
	 * @throws Exception
	 */
	public static SysWebControllerLog getControllerAnnotation(JoinPoint joinPoint) throws Exception {

		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		SysWebControllerLog logAnnotation = null;
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					logAnnotation = method.getAnnotation(SysWebControllerLog.class);
					break;
				}
			}
		}
		return logAnnotation;
	}

}
